package com.xuanyuanzi.all_learning.infrastructure.common.aop;

import cn.hutool.core.util.StrUtil;
import com.xuanyuanzi.all_learning.domain.model.sys.User;
import com.xuanyuanzi.all_learning.domain.service.AuthService;
import com.xuanyuanzi.all_learning.domain.service.UserService;
import com.xuanyuanzi.all_learning.infrastructure.common.enums.MustTokenEnum;
import com.xuanyuanzi.all_learning.infrastructure.common.thread.CurrentThread;
import com.xuanyuanzi.all_learning.infrastructure.common.utils.JwtTokenUtils;
import com.xuanyuanzi.all_learning.infrastructure.common.utils.SpringContextHolder;
import com.xuanyuanzi.all_learning.infrastructure.config.bean.SecurityProperties;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;

@Aspect
@Component
@Slf4j
@Order(2)
public class CheckAuthAspect {

    @Around(value = "@annotation(com.xuanyuanzi.all_learning.infrastructure.common.aop.JwtTokenAuth)")
    public Object aroundLogMethodPointcut(ProceedingJoinPoint pjp) throws Throwable {
        return handlePointcut(pjp);
    }

    @Around(value = "@within(com.xuanyuanzi.all_learning.infrastructure.common.aop.JwtTokenAuth)")
    public Object aroundLogClassPointcut(ProceedingJoinPoint pjp) throws Throwable {
        return handlePointcut(pjp);
    }

    public Object handlePointcut(ProceedingJoinPoint pjp) throws Throwable {
        UserService userService = SpringContextHolder.getApplicationContext().getBean(UserService.class);
        AuthService authService = SpringContextHolder.getApplicationContext().getBean(AuthService.class);

        JwtTokenAuth jwtTokenAuth;
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        jwtTokenAuth = method.getAnnotation(JwtTokenAuth.class);
        if (Objects.isNull(jwtTokenAuth)) {
            jwtTokenAuth = pjp.getTarget().getClass().getAnnotation(JwtTokenAuth.class);
        }
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (!Objects.isNull(requestAttributes)) {
            HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
            MustTokenEnum mustTokenEnum = jwtTokenAuth.mustToken();
            if (MustTokenEnum.NOT_MUST_TOKEN.equals(mustTokenEnum)) {
                User user = userService.getDefaultUser(jwtTokenAuth.value());
                if (Objects.isNull(user)) {
                    throw new Exception("用户未被找到");
                }
                CurrentThread.setUser(user);
                return pjp.proceed();
            }
            String authHeader = request.getHeader(JwtTokenUtils.AUTH_HEADER_KEY);
            if (StrUtil.isEmpty(authHeader)) {
                String token = request.getParameter(JwtTokenUtils.AUTH_TOKEN_KEY);
                authHeader = JwtTokenUtils.TOKEN_PREFIX + token;
            }
            log.info("## authHeader= {}", authHeader);
            if (StrUtil.isBlank(authHeader) || !authHeader.startsWith(JwtTokenUtils.TOKEN_PREFIX)) {
                throw new Exception("用户未登录");
            }
            // 获取token
            String token = authHeader.substring(JwtTokenUtils.TOKEN_PREFIX.length());
            if (StrUtil.isBlank(token)) {
                throw new Exception("用户未登录");
            }
            SecurityProperties securityProperties = SpringContextHolder.getApplicationContext().getBean(SecurityProperties.class);
            User user = authService.findByPkForJWT(JwtTokenUtils.getUserId(token, securityProperties.getBase64Secret()), jwtTokenAuth.value());
            if (user == null) {
                throw new Exception("登陆用户未被找到或已被禁用");
            }
            user.setClientType(jwtTokenAuth.value().getValue());
            CurrentThread.setUser(user);
            return pjp.proceed();
        }
        return pjp.proceed();
    }
}

